﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CloudClipboard
{
    public class CloudClipboardClient
    {
        private Socket socketClient = null;
        private string serverHost;
        private int serverPort;
        private byte[] recvBuffer = new byte[1024 * 1024]; //1M

        private const short shHead = 0x55AA;

        public event EventHandler<RecvPkgDataEventArgs> OnRecvComplete;
        public event EventHandler OnConnect;
        public event EventHandler OnClose;
        public event EventHandler OnSend;
        public event EventHandler OnRecving;

        public bool IsSending { get; private set; }
        public bool IsConnected { get; private set; }

        MemoryStream msRecv = new MemoryStream();

        public CloudClipboardClient(string host, int port)
        {
            this.serverHost = host;
            this.serverPort = port;
            this.IsSending = false;
            this.IsConnected = false;
        }

        public void ConnectToServer()
        {
            if (this.socketClient != null)
            {
                this.Close();
            }

            try
            {
                this.socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                this.socketClient.Connect(this.serverHost, this.serverPort);

                this.IsConnected = this.socketClient.Connected;

                System.Threading.ThreadPool.QueueUserWorkItem(obj =>
                {
                    this.LoopRead();
                });

                if (this.OnConnect != null)
                {
                    this.OnConnect(this, null);
                }

                //Thread newThread = new Thread(new ThreadStart(this.LoopRead));
                //newThread.SetApartmentState(ApartmentState.MTA);
                //newThread.Start();

                //Thread socketRecvThread = new Thread(new ParameterizedThreadStart(obj =>
                //{
                //    this.LoopRead();
                //}));

                //socketRecvThread.SetApartmentState(ApartmentState.STA);
                //socketRecvThread.Start();
            }
            catch (Exception ex)
            {
                this.Close();
                Debug.WriteLine(string.Format("connect err:{0}", ex.Message));
                return;
            }

        }

        public int SendData(byte[] bytes)
        {
            if (bytes == null || bytes.Length == 0)
            {
                return 0;
            }


            if (this.socketClient == null || this.socketClient.Connected == false)
            {
                this.ConnectToServer();
            }

            if (this.socketClient.Connected)
            {
                this.IsSending = true;

                // 包头
                byte[] byteHead = BitConverter.GetBytes(CloudClipboardClient.shHead);

                // 数据长度
                UInt32 dwDataLength = (UInt32)bytes.Length;
                byte[] byteDataLength = BitConverter.GetBytes(dwDataLength);

                MemoryStream ms = new MemoryStream();
                ms.Write(byteHead, 0, byteHead.Length);
                ms.Write(byteDataLength, 0, byteDataLength.Length);
                ms.Write(bytes, 0, bytes.Length);
                ms.Seek(0, SeekOrigin.Begin);

                int iSendLength = 0;
                byte[] buffer = new byte[ms.Length];

                ms.Read(buffer, 0, buffer.Length);

                // 完整包 
                while (iSendLength < buffer.Length)
                {
                    // Debug.WriteLine(string.Format("Start send, buffer:{0}", buffer.Length - iSendLength));

                    if (socketClient.Poll(100, SelectMode.SelectWrite))
                    {
                        try
                        {
                            iSendLength += socketClient.Send(buffer, iSendLength, buffer.Length - iSendLength, SocketFlags.None);
                             Debug.WriteLine(string.Format("send ok:{0}", iSendLength));
                        }
                        catch (Exception ex)
                        {
                            this.Close();
                            //Debug.WriteLine(string.Format("err:{0}", ex.Message));
                            break;
                        }
                    }
                }

                this.IsSending = false;

                if (this.OnSend != null)
                {
                    this.OnSend(this, null);
                }

                return iSendLength;
            }
            else
            {
                return 0;
            }
        }

        public void Close()
        {
            this.IsSending = false;
            this.IsConnected = false;

            if (this.socketClient != null)
            {
                this.socketClient.Close();
            }

            if (this.OnClose != null)
            {
                this.OnClose(this, null);
            }
        }

        private void LoopRead()
        {
            if (this.socketClient.Connected == true)
            {
                while (true)
                {
                    try
                    {
                        if (socketClient.Poll(100, SelectMode.SelectRead))
                        {
                            // read 
                            int iRecvLength = socketClient.Receive(recvBuffer, 1024 * 1024, SocketFlags.None);
                            Debug.WriteLine(string.Format("iRecvLength:{0}", iRecvLength));

                            if (iRecvLength > 0)
                            {
                                this.Recv_Raw_Data(recvBuffer, iRecvLength);
                            }
                            else
                            {
                                this.Close();
                            }
                        }

                        if (socketClient.Poll(10, SelectMode.SelectError))
                        {
                            this.Close();
                            Debug.WriteLine("SelectMode.SelectError");
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(string.Format("recv err:{0}", ex.Message));
                        this.Close();
                        break;
                    }
                }
            }
        }

        private void Recv_Pkg_Data(byte[] pkgData)
        {
            if (this.OnRecvComplete != null)
            {
                RecvPkgDataEventArgs eventArgs = new RecvPkgDataEventArgs()
                {
                    Data = pkgData
                };

                this.OnRecvComplete(this, eventArgs);
            }
        }

        private void Recv_Raw_Data(byte[] recvBuffer, int iRecvLength)
        {
            if (this.OnRecving != null)
            {
                this.OnRecving(this, null);
            }

            this.msRecv.Seek(0, SeekOrigin.End);
            this.msRecv.Write(recvBuffer, 0, iRecvLength);
            this.msRecv.Seek(0, SeekOrigin.Begin);

            byte[] baseBytes = this.msRecv.GetBuffer();
            int baseBytesPosition = 0;

            while (this.msRecv.Length > (sizeof(UInt16) + sizeof(UInt32)))
            {
                //找包头
                while (BitConverter.ToUInt16(baseBytes, baseBytesPosition) != CloudClipboardClient.shHead)
                {
                    baseBytesPosition++;
                    if ((this.msRecv.Length - baseBytesPosition) < (sizeof(UInt16) + sizeof(UInt32)))
                    {
                        //找不到包头
                        break;
                    }
                }

                if ((this.msRecv.Length - baseBytesPosition) < (sizeof(UInt16) + sizeof(UInt32)))
                {
                    //找不到包头
                    byte[] leftData = new byte[this.msRecv.Length - baseBytesPosition];
                    this.msRecv.Position = baseBytesPosition;
                    this.msRecv.Read(leftData, 0, leftData.Length);

                    this.msRecv.SetLength(0);
                    this.msRecv.Write(leftData, 0, leftData.Length);
                    break;
                }
                else
                {
                    //找到包头,获取包大小  ;
                    UInt32 pkgLength = BitConverter.ToUInt32(baseBytes, baseBytesPosition + sizeof(UInt16));

                    if ((baseBytesPosition + sizeof(UInt16) + sizeof(UInt32) + pkgLength) == this.msRecv.Length)
                    {
                        // 刚好收到一个完整包
                        byte[] pkgData = new byte[pkgLength];
                        this.msRecv.Position = baseBytesPosition + sizeof(UInt16) + sizeof(UInt32);
                        this.msRecv.Read(pkgData, 0, pkgData.Length);
                        this.Recv_Pkg_Data(pkgData);

                        this.msRecv.SetLength(0);
                    }
                    else if ((baseBytesPosition + sizeof(UInt16) + sizeof(UInt32) + pkgLength) < this.msRecv.Length)
                    {
                        // 收到一个以上的包
                        byte[] pkgData = new byte[pkgLength];
                        this.msRecv.Position = baseBytesPosition + sizeof(UInt16) + sizeof(UInt32);
                        this.msRecv.Read(pkgData, 0, (int)pkgLength);
                        this.Recv_Pkg_Data(pkgData);

                        byte[] leftData = new byte[this.msRecv.Length - baseBytesPosition - sizeof(UInt16) - sizeof(UInt32) - pkgLength];

                        this.msRecv.Position = baseBytesPosition + sizeof(UInt16) + sizeof(UInt32) + (int)pkgLength;
                        this.msRecv.Read(leftData, 0, leftData.Length);
                        this.msRecv.SetLength(0);
                        this.msRecv.Write(leftData, 0, leftData.Length);
                    }
                    else
                    {
                        //未收到完整的包
                        byte[] leftData = new byte[this.msRecv.Length - baseBytesPosition];
                        this.msRecv.Position = baseBytesPosition;
                        this.msRecv.Read(leftData, 0, leftData.Length);

                        this.msRecv.SetLength(0);
                        this.msRecv.Write(leftData, 0, leftData.Length);
                        break;
                    }
                }
            }
        }
    }
}
